package com.labofjet.common.aop;

import ch.qos.logback.classic.spi.ILoggingEvent;
import com.labofjet.common.dto.LogDto;
import com.labofjet.common.log.annotation.NoAopLog;
import com.labofjet.common.util.CommonUtils;
import com.labofjet.common.util.SpringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Aspect
@Order(AspectOrder.LOG_ORDER)
public class LogAspect extends BaseLogAspect {

	@Autowired
	private SpringUtils springUtils;

	private static final Map<ILoggingEvent, LogDto> bindMap = new ConcurrentHashMap<>(512);

	@Around(value = "within(@org.springframework.web.bind.annotation.RestController *))", argNames = "param")
	public Object controllerLog(ProceedingJoinPoint pjp) throws Throwable {
		Class<?> controller = pjp.getTarget().getClass();
		NoAopLog annotation = AnnotationUtils.findAnnotation(controller, NoAopLog.class);
		// 生成一个UUID丢到threadlocal里,便于追踪方法调用的log
		String traceId = CommonUtils.getTraceId(true);
		CommonUtils.setTraceId(traceId);
		Object result = null;
		if (annotation != null) { // 需要跳过AOP日志的话就跳过,但是手动日志还是要记录的
			return pjp.proceed();
		} else {
			result = doLog(pjp, "Controller");
		}
		// 用完了删除
		CommonUtils.removeTraceId();
		return result;
	}

	@Around(value = "within(@org.springframework.stereotype.Service *) && !within(@com.labofjet.common.log.annotation.NoAopLog *)", argNames = "param")
	public Object serviceLog(ProceedingJoinPoint pjp) throws Throwable {
		return doLog(pjp, "Service");
	}

	/**
	 * 在同步写日志的情况可以设置uuid,异步不行
	 *
	 * @return
	 */
	public static LogDto getLogDto() {
		LogDto logDto = new LogDto();
		logDto.setUuid(CommonUtils.getTraceId()); // 异步日志的话一般是null
		return logDto;
	}


	/**
	 * 异步日志用
	 *
	 * @param e
	 * @return
	 */
	public static LogDto getLogDto(ILoggingEvent e) {
		return bindMap.get(e);
	}

	/**
	 * 异步日志用
	 *
	 * @param e
	 * @return
	 */
	public static LogDto setLogDto(ILoggingEvent e, LogDto logDto) {
		return bindMap.put(e, logDto);
	}

	/**
	 * 清除ILoggingEvent对应的logDto
	 *
	 * @param e
	 */
	public static LogDto clean(ILoggingEvent e) {
		return bindMap.remove(e);
	}
}
